LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;

-------------------------------------- Forwarding_Prototype --------------------------------------------------

-- You guys are killing me.

ENTITY Forwarding_Prototype IS
   
    PORT(
       clr : IN STD_LOGIC;
       clk : IN STD_LOGIC;
       data_available0, data_available1, data_available2, data_available3 : IN STD_LOGIC;
       IN_RCV0_to_FWD_DATA, IN_RCV1_to_FWD_DATA, IN_RCV2_to_FWD_DATA, IN_RCV3_to_FWD_DATA	: IN STD_LOGIC_VECTOR(7 DOWNTO 0);
	   IN_RCV0_to_FWD_LENGTH, IN_RCV1_to_FWD_LENGTH, IN_RCV2_to_FWD_LENGTH, IN_RCV3_to_FWD_LENGTH  	  : IN STD_LOGIC_VECTOR(11 DOWNTO 0); --MSB is valid bit
       OUT_FWD_to_RCV0_LENGTH_ACK, OUT_FWD_to_RCV1_LENGTH_ACK, OUT_FWD_to_RCV2_LENGTH_ACK, OUT_FWD_to_RCV3_LENGTH_ACK  : OUT STD_LOGIC;
	   OUT_FWD_to_RCV0_FRAME_ACK, OUT_FWD_to_RCV1_FRAME_ACK, OUT_FWD_to_RCV2_FRAME_ACK, OUT_FWD_to_RCV3_FRAME_ACK	  : OUT STD_LOGIC;
       packet_size_comparator_out0, packet_size_comparator_out1, packet_size_comparator_out2, packet_size_comparator_out3 : OUT STD_LOGIC;
	   TBL_to_FWD_ACK : IN STD_LOGIC;
	   TBL_to_FWD_Valid : IN STD_LOGIC;
	   TBL_to_FWD_Port : IN STD_LOGIC_VECTOR(2 DOWNTO 0);
	   FWD_to_XMT0_ACK, FWD_to_XMT1_ACK, FWD_to_XMT2_ACK, FWD_to_XMT3_ACK : OUT STD_LOGIC;
	   XMT0_to_FWD_spaceavailable, XMT1_to_FWD_spaceavailable, XMT2_to_FWD_spaceavailable,XMT3_to_FWD_spaceavailable : IN STD_LOGIC_VECTOR(10 DOWNTO 0);
       IPv4_frame_count_out0, VLAN_frame_count_out0, IPv4_frame_count_out1, VLAN_frame_count_out1, IPv4_frame_count_out2, VLAN_frame_count_out2, IPv4_frame_count_out3, VLAN_frame_count_out3 	:	OUT STD_LOGIC_VECTOR(11 DOWNTO 0);

       -- For buffer0
       
       buffer_debug_data_out : OUT STD_LOGIC_VECTOR(7 DOWNTO 0); 
       buffer_debug_count_out	:	OUT STD_LOGIC_VECTOR(11 DOWNTO 0);
       buffer_debug_srcAddressValid : OUT STD_LOGIC;
       buffer_debug_desAddressValid : OUT STD_LOGIC;
       --buffer_debug_srcAddress : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
       --buffer_debug_desAddress : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
       buffer_debug_sendingsrcAddress, buffer_debug_sendingdesAddress : OUT STD_LOGIC;
       --buffer_debug_bufferSize : OUT STD_LOGIC_VECTOR(10 DOWNTO 0);
       buffer_debug_bufferDone : OUT STD_LOGIC;
       packetType0, packetType1, packetType2, packetType3 : OUT STD_LOGIC;
       
       -- For arbiter
       arbiter_debug_portChoice : OUT STD_LOGIC_VECTOR(1 DOWNTO 0);
	   arbiter_debug_portReady : OUT STD_LOGIC; -- Are any ports ready?
       
       -- For addbuff2

       FWD_to_TBL_desAddress, FWD_to_TBL_srcAddress : OUT STD_LOGIC_VECTOR(47 DOWNTO 0);
       FWD_to_TBL_srcPort : OUT STD_LOGIC_VECTOR(1 DOWNTO 0);
       FWD_to_TBL_ACK :OUT STD_LOGIC;
       debug_count0_of_IPv4_frames: OUT STD_LOGIC;
       
       -- For OutMUX
       
       OutMUX_debug_buffer0_request, OutMUX_debug_buffer1_request, OutMUX_debug_buffer2_request, OutMUX_debug_buffer3_request : OUT STD_LOGIC;
	   FWD_to_XMT0_DONE, FWD_to_XMT1_DONE, FWD_to_XMT2_DONE, FWD_to_XMT3_DONE : OUT STD_LOGIC;
	   FWD_to_XMT0_LENGTH, FWD_to_XMT1_LENGTH, FWD_to_XMT2_LENGTH, FWD_to_XMT3_LENGTH : OUT STD_LOGIC_VECTOR(11 DOWNTO 0);
	   FWD_to_XMT0_DATA, FWD_to_XMT1_DATA, FWD_to_XMT2_DATA, FWD_to_XMT3_DATA : OUT STD_LOGIC_VECTOR(7 DOWNTO 0)
       );
       
END Forwarding_Prototype;

ARCHITECTURE fwd_proto_arch OF Forwarding_Prototype IS
   
-------------------------------------- Components ----------------------------------------------- 


-------------------------------------- SaturatingCounter ----------------------------------------------

COMPONENT SaturatingCounter IS
   PORT(
	   incount, reset			 : IN STD_LOGIC;
       saturatedcount 		 	 : OUT STD_LOGIC_VECTOR(16 DOWNTO 0)
       );
END COMPONENT;

--------------------------------------- Buffer  ------------------------------------------------------

COMPONENT ForwardingBuffer IS
   PORT(
              clr : IN STD_LOGIC;
       clk : IN STD_LOGIC;
       data_available : IN STD_LOGIC;
       pass_data	:	IN	STD_LOGIC;
       IN_RCV_to_FWD_DATA	  	  : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
	   IN_RCV_to_FWD_LENGTH  	  : IN STD_LOGIC_VECTOR(11 DOWNTO 0); --MSB is valid bit
       OUT_FWD_to_RCV_LENGTH_ACK  : OUT STD_LOGIC;
	   OUT_FWD_to_RCV_FRAME_ACK	  : OUT STD_LOGIC;
       data_out : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
       count_out : OUT STD_LOGIC_VECTOR(11 DOWNTO 0);
       srcAddressValid : OUT STD_LOGIC;
       desAddressValid : OUT STD_LOGIC;
       packetType : OUT STD_LOGIC;
      -- srcPort : OUT STD_LOGIC_VECTOR(1 DOWNTO 0);
       srcAddress : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
       desAddress : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
       sendingsrcAddress, sendingdesAddress : OUT STD_LOGIC;
       packet_size_comparator_out : OUT STD_LOGIC;
       bufferSize : OUT STD_LOGIC_VECTOR(10 DOWNTO 0);
       bufferDone : OUT STD_LOGIC; -- Can we assume we're done if we have an empty buffer?
       IPv4_frame_count, VLAN_frame_count	:	OUT STD_LOGIC_VECTOR(11 DOWNTO 0)

       );
END COMPONENT;

-------------------------------------- Arbiter  ------------------------------------------------------

-- Add in a clock? OK.

COMPONENT Arbiter IS
   PORT(
       clock : IN STD_LOGIC;
       asreset : IN STD_LOGIC;
       tableheardarbiter : IN STD_LOGIC;
	   srcAddressValid0, srcAddressValid1, srcAddressValid2, srcAddressValid3 : IN STD_LOGIC;
	   desAddressValid0, desAddressValid1, desAddressValid2, desAddressValid3 : IN STD_LOGIC;
	   portChoice : OUT STD_LOGIC_VECTOR(1 DOWNTO 0);
	   portReady : OUT STD_LOGIC -- Are any ports ready?;
       );
END COMPONENT;

-------------------------------------- AddressMUX   ------------------------------------------------------

COMPONENT addbuff3 IS
   PORT(
       clk, reset :  IN STD_LOGIC;     -- clk
       portChoice : IN STD_LOGIC_VECTOR(1 DOWNTO 0); -- portChoice
       portvalid: IN STD_LOGIC; -- portReady
	   des_mux0, des_mux1,des_mux2,des_mux3 : IN STD_LOGIC_VECTOR(7 DOWNTO 0); -- desAddress0
	   src_mux0, src_mux1,src_mux2,src_mux3: IN STD_LOGIC_VECTOR(7 DOWNTO 0);  -- srcAddress0
	   WE_READY: OUT STD_LOGIC; -- ACK to table indicating that table can start reading the data
	   src_ready0, src_ready1, src_ready2, src_ready3 : IN STD_LOGIC; -- sendingsrcAddress0
	   des_ready0, des_ready1, des_ready2, des_ready3 : IN STD_LOGIC; -- sendingdesAddress0
	   srcPort : OUT STD_LOGIC_VECTOR(1 DOWNTO 0);	  -- FWD_to_TBL_srcPort
	   srcAddress : OUT STD_LOGIC_VECTOR(47 DOWNTO 0);-- FWD_to_TBL_srcAddress
	   desAddress : OUT STD_LOGIC_VECTOR(47 DOWNTO 0) -- FWD_to_TBL_desAddress
       );
END COMPONENT;

-------------------------------------- OutMUX   ------------------------------------------------------

COMPONENT OutMUX IS
   PORT(
		clk, reset : IN STD_LOGIC;
		buffer0_data, buffer1_data, buffer2_data, buffer3_data : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
		buffer0_size, buffer1_size, buffer2_size, buffer3_size : IN STD_LOGIC_VECTOR(10 DOWNTO 0);
		buffer0_done, buffer1_done, buffer2_done, buffer3_done : IN STD_LOGIC;
		TBL_to_FWD_Valid : IN STD_LOGIC;
		ARB_to_OMX_Port : IN STD_LOGIC_VECTOR(1 DOWNTO 0);
		ARB_to_OMX_Valid : IN STD_LOGIC;
		TBL_to_FWD_Port : IN STD_LOGIC_VECTOR(2 DOWNTO 0);		
		XMT0_size, XMT1_size, XMT2_size, XMT3_size : IN STD_LOGIC_VECTOR(10 DOWNTO 0);
		buffer0_request, buffer1_request, buffer2_request, buffer3_request : OUT STD_LOGIC;
		FWD_to_XMT0_ACK, FWD_to_XMT1_ACK, FWD_to_XMT2_ACK, FWD_to_XMT3_ACK : OUT STD_LOGIC;
		FWD_to_XMT0_Data, FWD_to_XMT1_Data, FWD_to_XMT2_Data, FWD_to_XMT3_Data : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
		FWD_to_XMT0_Size, FWD_to_XMT1_Size, FWD_to_XMT2_Size, FWD_to_XMT3_Size : OUT STD_LOGIC_VECTOR(11 DOWNTO 0);
		FWD_to_XMT0_Done, FWD_to_XMT1_Done, FWD_to_XMT2_Done, FWD_to_XMT3_Done : OUT STD_LOGIC
       );
END COMPONENT;


-------------------------------------- Signals  ------------------------------------------------------

-- COUNTERS
SIGNAL CountA, CountB, CountC : STD_LOGIC;
SIGNAL CounterA : STD_LOGIC_VECTOR(16 DOWNTO 0);
SIGNAL CounterB : STD_LOGIC_VECTOR(16 DOWNTO 0);
SIGNAL CounterC : STD_LOGIC_VECTOR(16 DOWNTO 0);

-- BUFFER
SIGNAL srcAddress0, srcAddress1, srcAddress2, srcAddress3 : STD_LOGIC_VECTOR(7 DOWNTO 0);
SIGNAL desAddress0, desAddress1, desAddress2, desAddress3 : STD_LOGIC_VECTOR(7 DOWNTO 0);
SIGNAL sendingsrcAddress0, sendingdesAddress0, sendingsrcAddress1, sendingdesAddress1, sendingsrcAddress2, sendingdesAddress2, sendingsrcAddress3, sendingdesAddress3 : STD_LOGIC;
SIGNAL bufferSize0, bufferSize1, bufferSize2, bufferSize3 : STD_LOGIC_VECTOR(10 DOWNTO 0);
SIGNAL bufferDone0, bufferDone1, bufferDone2, bufferDone3 : STD_LOGIC;
SIGNAL data_out0, data_out1, data_out2, data_out3 : STD_LOGIC_VECTOR(7 DOWNTO 0); 
SIGNAL count_out0, count_out1, count_out2, count_out3	:	STD_LOGIC_VECTOR(11 DOWNTO 0);
SIGNAL pass_data0, pass_data1, pass_data2, pass_data3	:	STD_LOGIC;


-- ARBITER
SIGNAL srcAddress0Valid, srcAddress1Valid, srcAddress2Valid, srcAddress3Valid : STD_LOGIC;
SIGNAL desAddress0Valid, desAddress1Valid, desAddress2Valid, desAddress3Valid : STD_LOGIC;
SIGNAL portChoice : STD_LOGIC_VECTOR(1 DOWNTO 0);
SIGNAL portReady  : STD_LOGIC;



-------------------------------------- The Actual Code -----------------------------------------------

BEGIN

----------------------------------------------- Buffering ---------------------------------------------

-- Take a variable length input from RCV and store it in a long sequence of buffers. Parse the source address
-- and destination address from the input as it passes into the buffer.
       
       
Buffer0: ForwardingBuffer PORT MAP (clr, clk, data_available0, pass_data0, IN_RCV0_to_FWD_DATA, IN_RCV0_to_FWD_LENGTH, OUT_FWD_to_RCV0_LENGTH_ACK,
OUT_FWD_to_RCV0_FRAME_ACK, data_out0, count_out0, srcAddress0Valid, desAddress0Valid, packetType0, srcAddress0, desAddress0, sendingsrcAddress0, sendingdesAddress0,
packet_size_comparator_out0, bufferSize0, bufferDone0, IPv4_frame_count_out0, VLAN_frame_count_out0);

buffer_debug_data_out <= data_out0;
buffer_debug_count_out <= count_out0;
buffer_debug_srcAddressValid <= srcAddress0Valid;
buffer_debug_desAddressValid <= desAddress0Valid;
--buffer_debug_srcAddress <= srcAddress0;
--buffer_debug_desAddress <= desAddress0;
buffer_debug_sendingsrcAddress <= sendingsrcAddress0; 
buffer_debug_sendingdesAddress <= sendingdesAddress0;
--buffer_debug_bufferSize <= bufferSize0;
buffer_debug_bufferDone <= bufferDone0;


Buffer1: ForwardingBuffer PORT MAP (clr, clk, data_available1, pass_data1, IN_RCV1_to_FWD_DATA, IN_RCV1_to_FWD_LENGTH, OUT_FWD_to_RCV1_LENGTH_ACK,
OUT_FWD_to_RCV1_FRAME_ACK, data_out1, count_out1, srcAddress1Valid, desAddress1Valid, packetType1, srcAddress1, desAddress1, sendingsrcAddress1, sendingdesAddress1,
packet_size_comparator_out1, bufferSize1, bufferDone1, IPv4_frame_count_out1, VLAN_frame_count_out1);

Buffer2: ForwardingBuffer PORT MAP (clr, clk, data_available2, pass_data2, IN_RCV2_to_FWD_DATA, IN_RCV2_to_FWD_LENGTH, OUT_FWD_to_RCV2_LENGTH_ACK,
OUT_FWD_to_RCV2_FRAME_ACK, data_out2, count_out2, srcAddress2Valid, desAddress2Valid, packetType2, srcAddress2, desAddress2, sendingsrcAddress2, sendingdesAddress2,
packet_size_comparator_out2, bufferSize2, bufferDone2, IPv4_frame_count_out2, VLAN_frame_count_out2);

Buffer3: ForwardingBuffer PORT MAP (clr, clk, data_available3, pass_data3, IN_RCV3_to_FWD_DATA, IN_RCV3_to_FWD_LENGTH, OUT_FWD_to_RCV3_LENGTH_ACK,
OUT_FWD_to_RCV3_FRAME_ACK, data_out3, count_out3, srcAddress3Valid, desAddress3Valid, packetType3, srcAddress3, desAddress3, sendingsrcAddress3, sendingdesAddress3,
packet_size_comparator_out3, bufferSize3, bufferDone3, IPv4_frame_count_out3, VLAN_frame_count_out3);


-----------------------------------------------  Arbiter  ---------------------------------------------

-- Asynchronously choose between four inputs. (Just straight-up logic.)

-- Is portReady the same as portValid?
Arbitrate: Arbiter PORT MAP (clk, clr, TBL_to_FWD_ACK, srcAddress0Valid, srcAddress1Valid, srcAddress2Valid, srcAddress3Valid,
desAddress0Valid, desAddress1Valid, desAddress2Valid, desAddress3Valid, portChoice, portReady);

arbiter_debug_portChoice <= portChoice;
arbiter_debug_portReady <= portReady;


-----------------------------------------------     ADDBUFF   ---------------------------------------------

AddressBuffer: addbuff3 PORT MAP (clk, TBL_to_FWD_ACK, portChoice, portReady, desAddress0, desAddress1, desAddress2,
desAddress3, srcAddress0, srcAddress1, srcAddress2, srcAddress3, FWD_to_TBL_ACK ,sendingsrcAddress0, sendingsrcAddress1,
sendingsrcAddress2, sendingsrcAddress3, sendingdesAddress0, sendingdesAddress1, sendingdesAddress2, sendingdesAddress3,
FWD_to_TBL_srcPort, FWD_to_TBL_srcAddress, FWD_to_TBL_desAddress);


-----------------------------------------------     MUX 2    ---------------------------------------------

OutMUXGo: OutMUX PORT MAP (clk, clr, data_out0, data_out1, data_out2, data_out3, 
bufferSize0, bufferSize1, bufferSize2, bufferSize3, bufferDone0, bufferDone1, bufferDone2, bufferDone3, 
TBL_to_FWD_Valid, portChoice, portReady, 
TBL_to_FWD_Port, XMT0_to_FWD_spaceavailable, XMT1_to_FWD_spaceavailable, XMT2_to_FWD_spaceavailable, XMT3_to_FWD_spaceavailable, 
pass_data0, pass_data1, pass_data2, pass_data3, FWD_to_XMT0_ACK, FWD_to_XMT1_ACK, FWD_to_XMT2_ACK, FWD_to_XMT3_ACK, 
FWD_to_XMT0_DATA, FWD_to_XMT1_DATA, FWD_to_XMT2_DATA, FWD_to_XMT3_DATA, FWD_to_XMT0_LENGTH, FWD_to_XMT1_LENGTH, FWD_to_XMT2_LENGTH, FWD_to_XMT3_LENGTH, 
FWD_to_XMT0_DONE, FWD_to_XMT1_DONE, FWD_to_XMT2_DONE, FWD_to_XMT3_DONE);

OutMUX_debug_buffer0_request <= pass_data0;
OutMUX_debug_buffer1_request <= pass_data1;
OutMUX_debug_buffer2_request <= pass_data2;
OutMUX_debug_buffer3_request <= pass_data3;


--------------------------------------  Interface with the XMT team -------------------------------------

--DummyXMT0Stage: Dummy_XMT PORT MAP (clk, reset, FWD_to_XMT0_ACK, FWD_to_XMT0_DATA, FWD_to_XMT0_LENGTH, FWD_to_XMT0_DONE, XMT0_to_FWD_ACK, XMT0_to_FWD_spaceavailable);
--DummyXMT1Stage: Dummy_XMT PORT MAP (clk, reset, FWD_to_XMT1_ACK, FWD_to_XMT1_DATA, FWD_to_XMT1_LENGTH, FWD_to_XMT1_DONE, XMT1_to_FWD_ACK, XMT1_to_FWD_spaceavailable);
--DummyXMT2Stage: Dummy_XMT PORT MAP (clk, reset, FWD_to_XMT2_ACK, FWD_to_XMT2_DATA, FWD_to_XMT2_LENGTH, FWD_to_XMT2_DONE, XMT2_to_FWD_ACK, XMT2_to_FWD_spaceavailable);
--DummyXMT3Stage: Dummy_XMT PORT MAP (clk, reset, FWD_to_XMT3_ACK, FWD_to_XMT3_DATA, FWD_to_XMT3_LENGTH, FWD_to_XMT3_DONE, XMT3_to_FWD_ACK, XMT3_to_FWD_spaceavailable);

-- We need to define some kind of device to poll XMT and see when they're done before we start sending them
-- all of our data. By the time we actually do useful stuff with Dummy_XMT, we have a port number (which doesn't
-- do anything in this prototype because we're only dealing with a single input and output port), a large buffer of
-- data, 16 bits holding our data size, and a large buffer holding all of our data. All we have to do is:
-- 1.) Write a procedure to poll XMT until they respond
-- 2.) Send them our data until we run out, at which point we send them another ACK to tell them we're done.
-- 3.) Also implement some means of logic to have the port number of table actually have something to do with
-- something.

--------------------------------------  Counters ----------------------------------------------------------

--IncrementCounterAStage: SaturatingCounter PORT MAP (CountA, reset, CounterA);
--IncrementCounterBStage: SaturatingCounter PORT MAP (CountB, reset, CounterA);
--IncrementCounterCStage: SaturatingCounter PORT MAP (CountC, reset, CounterA);

-- I don't remember what we're counting but I know we need to count three things. All we have to do is change how
-- CountA, CountB, and CountC get triggered everytime a page successfully goes to XMT and this'll work. (I have 
-- waveforms verifying it: SaturatingCounterWorks.png and SaturatingCounterWorks2.png).

END fwd_proto_arch;